// Copyright 2011-2012, Vanya Davidenko.
// Используемая кодировка: UTF-8.

#ifndef LIBV_DEFINE_H_
#define LIBV_DEFINE_H_

#include <exception>
#include <libv/check.h>
#include <libv/static_assert.h>



#define LIBV_DEFINE_SIMPLE_EXCEPTION(type)                                 \
    class type : public ::std::exception {                                 \
      public:                                                              \
        type() throw() : ::std::exception() {}                             \
        type(const ::std::string& msg) throw()                             \
            : ::std::exception(), msg_(msg) {}                             \
        virtual ~type() throw() {}                                         \
        virtual const char* what() const throw() { return msg_.c_str(); }  \
      private:                                                             \
        /*const*/ ::std::string msg_;                                      \
    };

// TODO: BOOST_MPL_ASSERT(boost::is_base_of(::std::exception, base))
#define LIBV_DEFINE_DERIVED_EXCEPTION(type, base)                            \
    class type : public base {                                               \
      public:                                                                \
        type() : base() {                                                    \
            /* Примитивная защита от базовых классов не унаследованных   */  \
            /* от ::std::exception. Ситуация, когда base имеет оператор  */  \
            /* преобразования типа в ::std::exception не рассматривается */  \
            /* (код усложняется и проще использовать boost::is_base_of), */  \
            /* однако, этот случай крайне маловероятен.                  */  \
            volatile size_t i =                                              \
                sizeof(static_cast< ::std::exception& >(*this));             \
            (void)(i);                                                       \
        }                                                                    \
        /* Реализация ::std::exception в Visual C++ содержит             */  \
        /* нестандартный конструктор, принимающий const char* const.     */  \
        /* В таком случае данный конструктор не скомпилируется.          */  \
        /* Однако это срабатывает как защита для использования более     */  \
        /* простого макроса LIBV_DEFINE_SIMPLE_EXCEPTION вместо          */  \
        /* использования данного с base == ::std::exception.             */  \
        type(const ::std::string& msg) : base(msg) {}                        \
        virtual ~type() throw() {}                                           \
        virtual const char* what() const throw() { return base::what(); }    \
    };


// Использование пары константно-перегруженных getter-ов, возвращающих
// ссылку на член, вместо открытых (public) членов, позволяет запрещать
// изменение, когда это необходимо.

// Использование макросов способствует уменьшению числа ошибок.
#define LIBV_DEFINE_BOTH_REFERENCE_GETTERS(type, name)  \
    type& name() { return name##_; }                    \
    const type& name() const { return name##_; }

#define LIBV_DEFINE_CONST_REFERENCE_GETTER(type, name)  \
    const type& name() const { return name##_; }

#define LIBV_DEFINE_GETTER_AND_SETTER(type, name)          \
    const type& name() const { return name##_; }           \
    void set_##name(const type& name) {  name##_ = name; }


#define LIBV_DEFINE_STL_CONTAINER_PROJECTION_SIZE_EMPTY_CAP(type, name)  \
    bool Empty() const { return name.empty(); }                          \
    size_t Size() const { return name.size(); }


#define LIBV_DEFINE_STL_CONTAINER_PROJECTION_CONST_ACCESSORS_CAP(type, name)  \
    const type::value_type& Front() const {                                   \
        LIBV_PRECOND(!name.empty());                                          \
        return name.front();                                                  \
    }                                                                         \
    const type::value_type& Back() const {                                    \
        LIBV_PRECOND(!name.empty());                                          \
        return name.back();                                                   \
    }                                                                         \
    const type::value_type& operator[](const size_t id) const {               \
        LIBV_PRECOND(id < name.size());                                       \
        return name[id];                                                      \
    }                                                                         \

#define LIBV_DEFINE_STL_CONTAINER_PROJECTION_MUTATING_ACCESSORS_CAP(type, name)\
    type::value_type& Front() {                                                \
        LIBV_PRECOND(!name.empty());                                           \
        return name.front();                                                   \
    }                                                                          \
    type::value_type& Back() {                                                 \
        LIBV_PRECOND(!name.empty());                                           \
        return name.back();                                                    \
    }                                                                          \
    type::value_type& operator[](const size_t id) {                            \
        LIBV_PRECOND(id < name.size());                                        \
        return name[id];                                                       \
    }

#define LIBV_DEFINE_STL_CONTAINER_PROJECTION_MUTATING_ITERATORS(type, name)  \
    typedef type::iterator iterator;                                         \
    iterator begin() {                                                       \
        return name.begin();                                                 \
    }                                                                        \
    iterator end() {                                                         \
        return name.end();                                                   \
    }

#define LIBV_DEFINE_STL_CONTAINER_PROJECTION_CONST_ITERATORS(type, name)  \
    typedef type::const_iterator const_iterator;                          \
    const_iterator begin() const {                                        \
        return name.begin();                                              \
    }                                                                     \
    const_iterator end() const {                                          \
        return name.end();                                                \
    }



#define LIBV_DEFINE_STL_CONTAINER_PROJECTION_CONST_ACCESSORS_CAP_TPL(type,  \
                                                                     name)  \
    const typename type::value_type& Front() const {                        \
        LIBV_PRECOND(!name.empty());                                        \
        return name.front();                                                \
    }                                                                       \
    const typename type::value_type& Back() const {                         \
        LIBV_PRECOND(!name.empty());                                        \
        return name.back();                                                 \
    }                                                                       \
    const typename type::value_type& operator[](const size_t id) const {    \
        LIBV_PRECOND(id < name.size());                                     \
        return name[id];                                                    \
    }                                                                       

#define LIBV_DEFINE_STL_CONTAINER_PROJECTION_MUTATING_ACCESSORS_CAP_TPL(type,  \
                                                                        name)  \
    typename type::value_type& Front() {                                       \
        LIBV_PRECOND(!name.empty());                                           \
        return name.front();                                                   \
    }                                                                          \
    typename type::value_type& Back() {                                        \
        LIBV_PRECOND(!name.empty());                                           \
        return name.back();                                                    \
    }                                                                          \
    typename type::value_type& operator[](const size_t id) {                   \
        LIBV_PRECOND(id < name.size());                                        \
        return name[id];                                                       \
    }

#define LIBV_DEFINE_STL_CONTAINER_PROJECTION_MUTATING_ITERATORS_TPL(type, name)\
    typedef typename type::iterator iterator;                                  \
    iterator begin() {                                                         \
        return name.begin();                                                   \
    }                                                                          \
    iterator end() {                                                           \
        return name.end();                                                     \
    }

#define LIBV_DEFINE_STL_CONTAINER_PROJECTION_CONST_ITERATORS_TPL(type, name)  \
    typedef typename type::const_iterator const_iterator;                     \
    const_iterator begin() const {                                            \
        return name.begin();                                                  \
    }                                                                         \
    const_iterator end() const {                                              \
        return name.end();                                                    \
    }



#define LIBV_DEFINE_MEMBER_LESS(type, member, name) struct {     \
    bool operator()(const type & lhv, const type & rhv) const {  \
        return lhv.member < rhv.member;                          \
    }                                                            \
} name;

#endif  // LIBV_DEFINE_H_
